package com.lumlord.common.mybatis;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import com.lumlord.common.annotation.mybatis.SqlInjectionType;

/*@Intercepts({ 
	@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
	@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class })
})*/

@Intercepts(value = {
		@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }) })
@Component
public class SqlInterceptor implements Interceptor {
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object[] args = invocation.getArgs();
		MappedStatement mappedStatement = (MappedStatement) args[0];
		// 获取参数
		Object param = args[1];
		BoundSql boundSql = mappedStatement.getBoundSql(param);
		String sql = boundSql.getSql();
	//	String commandName = mappedStatement.getSqlCommandType().name().toUpperCase();
		SqlInjectionType type = null;
		
		if (commandOption(sql,"INSERT")) {
			type = SqlInjectionType.INSERT;
		} else if (commandOption(sql,"UPDATE")) {
			type = SqlInjectionType.UPDATE;
		} else if (commandOption(sql,"DELETE")) {
			type = SqlInjectionType.DEL;
		} else if (commandOption(sql,"SELECT")) {
			type = SqlInjectionType.QUERY;
		}
		
		
		for (int index = 1; index < args.length; index++) {
			Object o = args[index];
			if (null == o) {
				continue;
			}
			Class clazz = o.getClass();
			if (clazz.isPrimitive() || clazz.isEnum() || clazz.isArray()) {
				continue;
			}

			List<Field> fields = getAllFields(clazz);
			for (Field field : fields) {
				SetColumnVal.execute(field, o, sql, type);
			}
		}
		return invocation.proceed();
	}
	
	
	private Boolean commandOption(String sql,String sqlType) {
		if(sql.toUpperCase().replaceAll("UPDATE_", "").indexOf(sqlType)!=-1) {
			return true;
		}
		return false;
	}
	
	
	private List<Field> getAllFields(Class clazz){
		List<Field> list = new ArrayList<Field>();
		Field[] fieldsParent = clazz.getSuperclass().getDeclaredFields();
		for (Field field : fieldsParent) {
			 list.add(field);
		}
		Field[] fieldsChild = clazz.getDeclaredFields();
		for (Field field : fieldsChild) {
			 list.add(field);
		}
		
		return list;
	}
	
	

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
	}

}